#include <stdio.h>
#include <pthread.h>

/*
 * Cache相关的知识，这是理解这个问题的关键所在
 */

struct 
{
    long a;             /* thread 1 访问 */
    long b;             /* thread 2 访问 */
    char data[64];
} data;

void *thread_1(void)
{
    int i ;

    for (i = 0; i < 1000000000; i++) {
        data.a = 0xAAAA;
    }
}

void *thread_2(void)
{
    int i ;

    for (i = 0; i < 1000000000; i++) {
        data.b = 0xBBBB;
    }
}

int main()
{
    pthread_t id1, id2;

    pthread_create(&id1, NULL, (void *)thread_1, NULL);
    pthread_create(&id2, NULL, (void *)thread_2, NULL);

    /* 需要进行CPU affinity 设置才能100%保证两个线程运行在不同的CPU上 */

    pthread_join(id1, NULL);
    pthread_join(id2, NULL);

    return 0;
}


#if 0

    /* 接 array/array-01.c  */

/* 
 * 多CPU系统上的Cache 
 */
    在SMP(对称多处理器)系统中，每个CPU core都有自己的本地Cache。

    每个CPU Core都有自己的独立的L1 Cache，Core 0和Core 1共享L2 Cache，Core 2和Core 3共享L2 Cache，
    而L3 Cache则是在所有的CPU core之间共享。

/* 
 * Cache一致性 
 */ 
    因此，在多CPU系统上，同一个地址的数据，比如同一个变量，有可能在多个CPU的本地Cache中存在多份拷贝。
    既然同一数据存在多份拷贝，那么就必然存在数据一致性问题。

    简单来说，就是必须要保证，在任意时间点，所有CPU看到的同一个地址的数据，必须是一样的。
    换言之，就是必须要保证每个CPU的本地Cache中的数据，能够如实反映内存中数据的真实的值。

    假设内存中一个变量A = 1, 在core 0 和core 1的本地Cache中都有一份拷贝A0 = 1 和 A1 = 1。
    如果此时core 0需要修改变量的值，至少需要这些操作：

        1.  在Core 0本地Cache中修改：A0 = 100

        2.  把变量最新的值更新到内存中：A = 100

        3.  通过某种方式，通知core 1。比如，把core 1本地的Cache的数据标记为无效，
        这样下次core 1从Cache访问A1的时候，发现数据已经无效，便会触发Cache Miss，
        重新从内存中读取最新的值，并把最新的值更新到本地Cache。

    CPU为了保证Cache的一致性，实现了非常复杂的Cache一致性协议，可以去了解下MESI协议


/* 
 * 交换两个字段，到底影响了什么 
 */
    test1.c和test2.c几乎一模一样：有两个线程，分别访问一个struct全局变量的两个不同字段。
    唯一的区别是：test1.c中，字段a和b是相邻的：test2.c中，字段a和b是分开的：

    看起来，test1.c中两个字段a和b紧挨着的，按道理说，空间局部性应该更好，对吧？那为什么它的性能会比test2.c差那么多呢？
    其实，这是由于CPU为了保证Cache的一致性，而引起的Cache伪共享问题！

/* 
 * Cache伪共享(Cache False Sharing) 
 */
    所谓Cache Line 伪共享，是由于运行在不同CPU上的不同线程，同时修改同一个Cache Line上的数据，
    导致对方Cache Line失效，从而引起频繁的Cache Miss。

    数据在Cache和内存之间传输是以Cache Line为单位的，所以，即便每个CPU各自修改的是不同的变量，
    但只要这些变量是在同一个Cache Line上的，那么一个CPU修改了这个Cashe Line之后，为了Cache一致性，
    必然会导致另外一个CPU的本地Cache中的同一个Cache Line无效，进而引发Cache Miss。

    运行在不同CPU的多个线程，如果频繁修改位于同一个Cache Line上的数据（即便不是同一个地址的数据），
    必然会导致大量Cache Miss，严重降低程序性能。


/*
 * 性能差异的真正原因
 */
    test1.c中，字段a和b在同一个Cache Line上，运行于不同CPU的两个线程同时修改这两个字段时，
    会相互导致对方CPU的本地Cache Line频繁失效，触发大量Cache Miss，从而性能非常差！

    test2.c中，字段a和b中间隔了一个64字节的数组，从而确保了data.a和data.b分别处在不同的Cache Line上。
    如此一来，即便是两个线程同时修改这两个字段，但是他们是不同的Cache Line，也不会相互干扰，
    大大提高Cache命中率，甚至几乎不会出现Cache Miss，这就是为什么会有如此巨大的性能提升！

/*
 * 如何检测和避免Cache伪共享
 */
    在Linux上，可以使用perf c2c工具来检测和分析Cache伪共享的问题。

    要避免Cache伪共享问题，最简单的方式是把不同线程频繁修改的数据放在不同Cache Line上，
    如本文例子中，在字段a和b之间填充了64个字节的数组，以此来保证两个字段一定位于不同Cache Line中。
    实际项目中，要根据具体场景进行分析、解决。

#endif
